#include "udp_common.hpp"
#include <thread>
#include<sstream>
#include<unordered_map>
class UdpServer {
public:
    UdpServer(int port) : port_(port) {}

    void run() {
        sock_ = socket(AF_INET, SOCK_DGRAM, 0);
        if (sock_ < 0) { perror("socket"); exit(1); }

        sockaddr_in addr{};
        addr.sin_family = AF_INET;
        addr.sin_addr.s_addr = INADDR_ANY;
        addr.sin_port = htons(port_);

        if (bind(sock_, (sockaddr*)&addr, sizeof(addr)) < 0) {
            perror("bind"); exit(1);
        }

        std::cout << "[UDP Server] listening on port " << port_ << " ...\n";

        // 维护客户端的序列号
        std::unordered_map<std::string, int> clientSeqNums;

        while (true) {
            sockaddr_in client{};
            socklen_t len = sizeof(client);
            char buf[BUF_SIZE];

            ssize_t n = recvfrom(sock_, buf, BUF_SIZE - 1, 0, (sockaddr*)&client, &len);
            if (n <= 0) continue;

            buf[n] = '\0';
            std::string clientIp = inet_ntoa(client.sin_addr);
            int clientPort = ntohs(client.sin_port);

            std::cout << "[UDP Server] from " << clientIp << ":" << clientPort
                      << " -> " << buf << '\n';

            // 解析序列号和内容
            int seqNum;
            std::string content;
            std::istringstream iss(buf);
            iss >> seqNum;
            std::getline(iss, content);

            // 检查序列号
            //clientKey:"192.168.1.100:8080"
            //仅靠 IP 可能重复（同一机器多进程）
            //仅靠端口也可能重复（NAT 后多台机器）
            //但 IP+端口 组合在 同一时刻 基本可以唯一标识一条 UDP 会话
            std::string clientKey = clientIp + ":" + std::to_string(clientPort);
            if (clientSeqNums.  find(clientKey) == clientSeqNums.end()) {
                clientSeqNums[clientKey] = seqNum;
            }
            //下一条到达的序列号正好是上一条 +1, 只有这样才表示 没有丢包、顺序正确
            
             else if (seqNum != clientSeqNums[clientKey] + 1) {
                std::cout << "[UDP Server] Missing packets from " << clientKey << '\n';
                // 请求重传
                std::ostringstream oss;
                oss << "ACK:" << clientSeqNums[clientKey];
                std::string ack = oss.str();
                sendto(sock_, ack.c_str(), ack.size(), 0, (sockaddr*)&client, len);
                continue;
            }

            // 更新序列号并发送确认
            clientSeqNums[clientKey] = seqNum;
            std::string ack = "ACK";
            sendto(sock_, ack.c_str(), ack.size(), 0, (sockaddr*)&client, len);
        }
    }

private:
    int port_;
    int sock_;
};


int main() 
{
    //服务器必须“公布”自己的端口号（有时还要公布 IP），否则客户端根本不知道把数据包扔到哪；
    //至于服务器本身，端口号是 自己 bind 时指定的，IP 在监听 INADDR_ANY 时由内核自动关联到本机所有网卡
    UdpServer srv(PORT);
    srv.run();
    return 0;
}









